import Vue from 'vue'
import { hasClass, addClass, removeClass } from 'element-ui/src/utils/dom'
import ElCheckbox from 'element-ui/packages/checkbox'
import FilterPanel from './filter-panel.vue'
import LayoutObserver from './layout-observer'
import { mapStates } from './store/helper'

const getAllColumns = (columns) => {
  const result = []
  columns.forEach((column) => {
    if (column.children) {
      result.push(column)
      result.push.apply(result, getAllColumns(column.children))
    } else {
      result.push(column)
    }
  })
  return result
}

const convertToRows = (originColumns) => {
  let maxLevel = 1
  const traverse = (column, parent) => {
    if (parent) {
      column.level = parent.level + 1
      if (maxLevel < column.level) {
        maxLevel = column.level
      }
    }
    if (column.children) {
      let colSpan = 0
      column.children.forEach((subColumn) => {
        traverse(subColumn, column)
        colSpan += subColumn.colSpan
      })
      column.colSpan = colSpan
    } else {
      column.colSpan = 1
    }
  }

  originColumns.forEach((column) => {
    column.level = 1
    traverse(column)
  })

  const rows = []
  for (let i = 0; i < maxLevel; i++) {
    rows.push([])
  }

  const allColumns = getAllColumns(originColumns)

  allColumns.forEach((column) => {
    if (!column.children) {
      column.rowSpan = maxLevel - column.level + 1
    } else {
      column.rowSpan = 1
    }
    rows[column.level - 1].push(column)
  })

  return rows
}

export default {
  name: 'vbtTableHeader',

  mixins: [LayoutObserver],

  render(h) {
    const originColumns = this.store.states.originColumns
    const columnRows = convertToRows(originColumns, this.columns)
    // 是否拥有多级表头
    const isGroup = columnRows.length > 1
    if (isGroup) this.$parent.isGroup = true
    return (
      <table
        class='el-table__header'
        cellspacing='0'
        cellpadding='0'
        border='0'
      >
        <colgroup>
          {this.columns.map((column) => (
            <col name={column.id} key={column.id} />
          ))}
          {this.hasGutter ? <col name='gutter' /> : ''}
        </colgroup>
        <thead class={[{ 'is-group': isGroup, 'has-gutter': this.hasGutter }]}>
          {this._l(columnRows, (columns, rowIndex) => (
            <tr
              style={this.getHeaderRowStyle(rowIndex)}
              class={this.getHeaderRowClass(rowIndex)}
            >
              {columns.map((column, cellIndex) => (
                <th
                  colspan={column.colSpan}
                  rowspan={column.rowSpan}
                  on-mousemove={($event) =>
                    this.handleMouseMove($event, column)
                  }
                  on-mouseout={this.handleMouseOut}
                  on-mousedown={($event) =>
                    this.handleMouseDown($event, column)
                  }
                  on-click={($event) => this.handleHeaderClick($event, column)}
                  on-contextmenu={($event) =>
                    this.handleHeaderContextMenu($event, column)
                  }
                  style={this.getHeaderCellStyle(
                    rowIndex,
                    cellIndex,
                    columns,
                    column
                  )}
                  class={this.getHeaderCellClass(
                    rowIndex,
                    cellIndex,
                    columns,
                    column
                  )}
                  key={column.id}
                >
                  <div
                    class={[
                      'cell',
                      column.filteredValue && column.filteredValue.length > 0
                        ? 'highlight'
                        : '',
                      column.labelClassName,
                    ]}
                  >
                    {column.renderHeader
                      ? column.renderHeader.call(this._renderProxy, h, {
                          column,
                          $index: cellIndex,
                          store: this.store,
                          _self: this.$parent.$vnode.context,
                        })
                      : column.label}
                    {column.sortable ? (
                      <span
                        class='caret-wrapper'
                        on-click={($event) =>
                          this.handleSortClick($event, column)
                        }
                      >
                        <i
                          class='sort-caret ascending'
                          on-click={($event) =>
                            this.handleSortClick($event, column, 'ascending')
                          }
                        ></i>
                        <i
                          class='sort-caret descending'
                          on-click={($event) =>
                            this.handleSortClick($event, column, 'descending')
                          }
                        ></i>
                      </span>
                    ) : (
                      ''
                    )}
                    {column.filterable ? (
                      <span
                        class='el-table__column-filter-trigger'
                        on-click={($event) =>
                          this.handleFilterClick($event, column)
                        }
                      >
                        <i
                          class={[
                            'el-icon-arrow-down',
                            column.filterOpened ? 'el-icon-arrow-up' : '',
                          ]}
                        ></i>
                      </span>
                    ) : (
                      ''
                    )}
                  </div>
                </th>
              ))}
              {this.hasGutter ? <th class='gutter'></th> : ''}
            </tr>
          ))}
        </thead>
      </table>
    )
  },

  props: {
    fixed: String,
    store: {
      required: true,
    },
    border: Boolean,
    defaultSort: {
      type: Object,
      default() {
        return {
          prop: '',
          order: '',
        }
      },
    },
  },

  components: {
    ElCheckbox,
  },

  computed: {
    table() {
      return this.$parent
    },

    hasGutter() {
      return !this.fixed && this.tableLayout.gutterWidth
    },

    ...mapStates({
      columns: 'columns',
      isAllSelected: 'isAllSelected',
      leftFixedLeafCount: 'fixedLeafColumnsLength',
      rightFixedLeafCount: 'rightFixedLeafColumnsLength',
      columnsCount: (states) => states.columns.length,
      leftFixedCount: (states) => states.fixedColumns.length,
      rightFixedCount: (states) => states.rightFixedColumns.length,
    }),
  },

  created() {
    this.filterPanels = {}
  },

  mounted() {
    const { prop, order } = this.defaultSort
    const init = true
    this.store.commit('sort', { prop, order, init })
  },

  beforeDestroy() {
    const panels = this.filterPanels
    for (const prop in panels) {
      if (panels.hasOwnProperty(prop) && panels[prop]) {
        panels[prop].$destroy(true)
      }
    }
  },

  methods: {
    isCellHidden(index, columns) {
      let start = 0
      for (let i = 0; i < index; i++) {
        start += columns[i].colSpan
      }
      const after = start + columns[index].colSpan - 1
      if (this.fixed === true || this.fixed === 'left') {
        return after >= this.leftFixedLeafCount
      } else if (this.fixed === 'right') {
        return start < this.columnsCount - this.rightFixedLeafCount
      } else {
        return (
          after < this.leftFixedLeafCount ||
          start >= this.columnsCount - this.rightFixedLeafCount
        )
      }
    },

    getHeaderRowStyle(rowIndex) {
      const headerRowStyle = this.table.headerRowStyle
      if (typeof headerRowStyle === 'function') {
        return headerRowStyle({ rowIndex })
      }
      return headerRowStyle
    },

    getHeaderRowClass(rowIndex) {
      const classes = []

      const headerRowClassName = this.table.headerRowClassName
      if (typeof headerRowClassName === 'string') {
        classes.push(headerRowClassName)
      } else if (typeof headerRowClassName === 'function') {
        classes.push(headerRowClassName({ rowIndex }))
      }

      return classes.join(' ')
    },

    getHeaderCellStyle(rowIndex, columnIndex, row, column) {
      const headerCellStyle = this.table.headerCellStyle
      if (typeof headerCellStyle === 'function') {
        return headerCellStyle({
          rowIndex,
          columnIndex,
          row,
          column,
        })
      }
      return headerCellStyle
    },

    getHeaderCellClass(rowIndex, columnIndex, row, column) {
      const classes = [
        column.id,
        column.order,
        column.headerAlign,
        column.className,
        column.labelClassName,
      ]

      if (rowIndex === 0 && this.isCellHidden(columnIndex, row)) {
        classes.push('is-hidden')
      }

      if (!column.children) {
        classes.push('is-leaf')
      }

      if (column.sortable) {
        classes.push('is-sortable')
      }

      const headerCellClassName = this.table.headerCellClassName
      if (typeof headerCellClassName === 'string') {
        classes.push(headerCellClassName)
      } else if (typeof headerCellClassName === 'function') {
        classes.push(
          headerCellClassName({
            rowIndex,
            columnIndex,
            row,
            column,
          })
        )
      }

      return classes.join(' ')
    },

    toggleAllSelection(event) {
      event.stopPropagation()
      this.store.commit('toggleAllSelection')
    },

    handleFilterClick(event, column) {
      event.stopPropagation()
      const target = event.target
      let cell = target.tagName === 'TH' ? target : target.parentNode
      cell = cell.querySelector('.el-table__column-filter-trigger') || cell
      const table = this.$parent

      let filterPanel = this.filterPanels[column.id]

      if (filterPanel && column.filterOpened) {
        filterPanel.showPopper = false
        return
      }

      if (!filterPanel) {
        filterPanel = new Vue(FilterPanel)
        this.filterPanels[column.id] = filterPanel
        if (column.filterPlacement) {
          filterPanel.placement = column.filterPlacement
        }
        filterPanel.table = table
        filterPanel.cell = cell
        filterPanel.column = column
        !this.$isServer && filterPanel.$mount(document.createElement('div'))
      }

      setTimeout(() => {
        filterPanel.showPopper = true
      }, 16)
    },

    handleHeaderClick(event, column) {
      if (!column.filters && column.sortable) {
        this.handleSortClick(event, column)
      } else if (column.filterable && !column.sortable) {
        this.handleFilterClick(event, column)
      }

      this.$parent.$emit('header-click', column, event)
    },

    handleHeaderContextMenu(event, column) {
      this.$parent.$emit('header-contextmenu', column, event)
    },

    handleMouseDown(event, column) {
      if (this.$isServer) return
      if (column.children && column.children.length > 0) return
      /* istanbul ignore if */
      if (this.draggingColumn && this.border) {
        this.dragging = true

        this.$parent.resizeProxyVisible = true

        const table = this.$parent
        const tableEl = table.$el
        const tableLeft = tableEl.getBoundingClientRect().left
        const columnEl = this.$el.querySelector(`th.${column.id}`)
        const columnRect = columnEl.getBoundingClientRect()
        const minLeft = columnRect.left - tableLeft + 30

        addClass(columnEl, 'noclick')

        this.dragState = {
          startMouseLeft: event.clientX,
          startLeft: columnRect.right - tableLeft,
          startColumnLeft: columnRect.left - tableLeft,
          tableLeft,
        }

        const resizeProxy = table.$refs.resizeProxy
        resizeProxy.style.left = this.dragState.startLeft + 'px'

        document.onselectstart = function () {
          return false
        }
        document.ondragstart = function () {
          return false
        }

        const handleMouseMove = (event) => {
          const deltaLeft = event.clientX - this.dragState.startMouseLeft
          const proxyLeft = this.dragState.startLeft + deltaLeft

          resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px'
        }

        const handleMouseUp = () => {
          if (this.dragging) {
            const { startColumnLeft, startLeft } = this.dragState
            const finalLeft = parseInt(resizeProxy.style.left, 10)
            const columnWidth = finalLeft - startColumnLeft
            column.width = column.realWidth = columnWidth
            table.$emit(
              'header-dragend',
              column.width,
              startLeft - startColumnLeft,
              column,
              event
            )

            this.store.scheduleLayout()

            document.body.style.cursor = ''
            this.dragging = false
            this.draggingColumn = null
            this.dragState = {}

            table.resizeProxyVisible = false
          }

          document.removeEventListener('mousemove', handleMouseMove)
          document.removeEventListener('mouseup', handleMouseUp)
          document.onselectstart = null
          document.ondragstart = null

          setTimeout(function () {
            removeClass(columnEl, 'noclick')
          }, 0)
        }

        document.addEventListener('mousemove', handleMouseMove)
        document.addEventListener('mouseup', handleMouseUp)
      }
    },

    handleMouseMove(event, column) {
      if (column.children && column.children.length > 0) return
      let target = event.target
      while (target && target.tagName !== 'TH') {
        target = target.parentNode
      }

      if (!column || !column.resizable) return

      if (!this.dragging && this.border) {
        const rect = target.getBoundingClientRect()

        const bodyStyle = document.body.style
        if (rect.width > 12 && rect.right - event.pageX < 8) {
          bodyStyle.cursor = 'col-resize'
          if (hasClass(target, 'is-sortable')) {
            target.style.cursor = 'col-resize'
          }
          this.draggingColumn = column
        } else if (!this.dragging) {
          bodyStyle.cursor = ''
          if (hasClass(target, 'is-sortable')) {
            target.style.cursor = 'pointer'
          }
          this.draggingColumn = null
        }
      }
    },

    handleMouseOut() {
      if (this.$isServer) return
      document.body.style.cursor = ''
    },

    toggleOrder({ order, sortOrders }) {
      if (order === '') return sortOrders[0]
      const index = sortOrders.indexOf(order || null)
      return sortOrders[index > sortOrders.length - 2 ? 0 : index + 1]
    },

    handleSortClick(event, column, givenOrder) {
      event.stopPropagation()
      const order =
        column.order === givenOrder
          ? null
          : givenOrder || this.toggleOrder(column)

      let target = event.target
      while (target && target.tagName !== 'TH') {
        target = target.parentNode
      }

      if (target && target.tagName === 'TH') {
        if (hasClass(target, 'noclick')) {
          removeClass(target, 'noclick')
          return
        }
      }

      if (!column.sortable) return

      const states = this.store.states
      let sortProp = states.sortProp
      let sortOrder
      const sortingColumn = states.sortingColumn

      if (
        sortingColumn !== column ||
        (sortingColumn === column && sortingColumn.order === null)
      ) {
        if (sortingColumn) {
          sortingColumn.order = null
        }
        states.sortingColumn = column
        sortProp = column.property
      }

      if (!order) {
        sortOrder = column.order = null
      } else {
        sortOrder = column.order = order
      }

      states.sortProp = sortProp
      states.sortOrder = sortOrder

      this.store.commit('changeSortCondition')
    },
  },

  data() {
    return {
      draggingColumn: null,
      dragging: false,
      dragState: {},
    }
  },
}
